// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.

package cn.birdbaby.plugins.annotator.classannotator.gen;

import com.intellij.codeInsight.actions.ReformatFilesOptions;
import com.intellij.formatting.FormattingModelBuilder;
import com.intellij.ide.lightEdit.LightEditCompatible;
import com.intellij.lang.LanguageFormatting;
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.project.DumbAware;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiUtilCore;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;

import java.util.Arrays;
import java.util.List;

/**
 * 1. 获取需要操作的psiElement：java文件/dir
 * 2. 判断当前需要操作的psiElement是不是domain entity && 判断当前element中是否存在Gen注解
 * 3. 添加Gen注解
 */
public class BBAddGenAnnotationsAction extends AnAction implements DumbAware, LightEditCompatible {
    private static final Logger LOG = Logger.getInstance(BBAddGenAnnotationsAction.class);

    private static ReformatFilesOptions myTestOptions;

    public BBAddGenAnnotationsAction() {
        setEnabledInModalContext(true);
    }


    public static PsiFile @NotNull [] convertToPsiFiles(VirtualFile @NotNull [] files, @NotNull Project project) {
        PsiManager psiManager = PsiManager.getInstance(project);
        List<PsiFile> list = PsiUtilCore.toPsiFiles(psiManager, Arrays.asList(files));
        return PsiUtilCore.toPsiFileArray(list);
    }

    private static boolean isActionAvailable(@NotNull AnActionEvent event) {
        DataContext dataContext = event.getDataContext();
        Project project = CommonDataKeys.PROJECT.getData(dataContext);
        if (project == null) {
            return false;
        }

        Editor editor = CommonDataKeys.EDITOR.getData(dataContext);

        final VirtualFile[] files = CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext);

        if (editor != null) {
            PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
            if (file == null || file.getVirtualFile() == null) {
                return false;
            }

            if (LanguageFormatting.INSTANCE.forContext(file) != null) {
                return true;
            }
        } else if (files != null && containsOnlyFiles(files)) {
            boolean anyFormatters = false;
            for (VirtualFile virtualFile : files) {
                final PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);
                if (psiFile == null) {
                    return false;
                }
                final FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(psiFile);
                if (builder != null) {
                    anyFormatters = true;
                    break;
                }
            }
            if (!anyFormatters) {
                return false;
            }
        } else if (files != null && files.length == 1) {
            // skip. Both directories and single files are supported.
        } else if (LangDataKeys.MODULE_CONTEXT.getData(dataContext) == null &&
                PlatformCoreDataKeys.PROJECT_CONTEXT.getData(dataContext) == null) {
            PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
            if (element == null) {
                return false;
            }
            if (!(element instanceof PsiDirectory)) {
                PsiFile file = element.getContainingFile();
                if (file == null || LanguageFormatting.INSTANCE.forContext(file) == null) {
                    return false;
                }
            }
        }
        return true;
    }

    static boolean containsOnlyFiles(VirtualFile @NotNull [] files) {
        if (files.length < 1) return false;
        for (VirtualFile virtualFile : files) {
            if (virtualFile.isDirectory()) return false;
        }
        return true;
    }

    @Override
    public void actionPerformed(@NotNull AnActionEvent event) {
        DataContext dataContext = event.getDataContext();
        final Project project = CommonDataKeys.PROJECT.getData(dataContext);
        if (project == null) {
            return;
        }
        final Editor editor = CommonDataKeys.EDITOR.getData(dataContext);
        final VirtualFile[] files = CommonDataKeys.VIRTUAL_FILE_ARRAY.getData(dataContext);

        PsiFile file = null;
        PsiDirectory dir;

        //打开编辑器了，直接操作当前编辑器的文件
        if (editor != null) {
            file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument());
            if (file == null) return;
            dir = file.getContainingDirectory();
        } else {
            PsiElement element = CommonDataKeys.PSI_ELEMENT.getData(dataContext);
            if (element == null) return;
            if (element instanceof PsiDirectoryContainer) {
                dir = ArrayUtil.getFirstElement(((PsiDirectoryContainer) element).getDirectories());
            } else if (element instanceof PsiDirectory) {
                dir = (PsiDirectory) element;
            } else {
                file = element.getContainingFile();
                if (file == null) return;
                dir = file.getContainingDirectory();
            }
        }
       if (file == null && dir != null) {
            //清空缓冲区，减少并发修改产生的冲突
            PsiDocumentManager.getInstance(project).commitAllDocuments();
            new BBAddGenAnnotationsProcessor(project, editor, List.of(convertToPsiFiles(files, project))).processFiles();
        } else {
            //清缓存
            PsiDocumentManager.getInstance(project).commitDocument(editor.getDocument());

            //最后才会跑进这里来
            new BBAddGenAnnotationsProcessor(project, editor, file).processCode();
        }
    }

    @Override
    public @NotNull ActionUpdateThread getActionUpdateThread() {
        return ActionUpdateThread.BGT;
    }

    @Override
    public void update(@NotNull AnActionEvent event) {
        Presentation presentation = event.getPresentation();
        boolean available = isActionAvailable(event);
        if (event.isFromContextMenu()) {
            presentation.setEnabledAndVisible(available);
        } else {
            presentation.setEnabled(available);
        }
    }
}